package MySQL::Query::Extract::WHERE;

use strict;
use warnings;

use base qw(MySQL::Query::Extract);
use Carp;
use SQL::Builder::Where;

sub parse_tree     { return shift->tree->tree }
sub tokens         { return shift->tree->tokens(@_) }

# eh... OMG
sub process {
      my ($self , $sel , $extract , $callback) = @_;
      
      $callback = "table_comp" unless $callback;
      
      my $where_tree = $self->parse_tree->getWhere();
      $where_tree = $where_tree->shrink;
      if ($where_tree) {
	    my $tokens = $self->tokens($where_tree);
	    my $where_struct = $self->build_where_struct($tokens);
	    
            use Data::Dumper;
            #print Dumper $where_struct;
            
            my $table_spec_tree = $self->filter_conditions($where_struct , $callback , $extract);
	    my $where_sel = $self->build_where_sql_class($table_spec_tree);
	    
	    $sel->where->list_push($where_sel);
      }
}

sub unknown_conditions {
      my ($self , $extract , $inner) = @_;
      
      my $where_tree = $self->parse_tree->getWhere();
      $where_tree = $where_tree->shrink;
      
      if ($where_tree) {
	    my $tokens = $self->tokens($where_tree);
	    my $where_struct = $self->build_where_struct($tokens);
	    return $self->unknown_conds($where_struct , $extract , $inner);
      }
}

sub build_where_struct {
      my ($self, $tokens_tree , $num) = @_;
      my @struct = ();
      
      no warnings;
      for ($self->{where_iter} = $num; $self->{where_iter} <= $#{$tokens_tree}; $self->{where_iter}++) {
	    if ($tokens_tree->[$self->{where_iter}]->type() eq '(') {
                  push @struct , $self->build_where_struct($tokens_tree , $self->{where_iter} + 1);
	    } elsif ($tokens_tree->[$self->{where_iter}]->type() eq ')') {
                  return \@struct;
	    } else {
                  if (	$tokens_tree->[$self->{where_iter}]->type() eq 'IDENT' &&  # 
			$tokens_tree->[$self->{where_iter} + 1]->type() eq '.' &&  #   table1.column1
			$tokens_tree->[$self->{where_iter} + 2]->type() eq 'IDENT' # 
		  ) {
			my %temp = ();
			my $table_column = $tokens_tree->[$self->{where_iter}]->value() . "." . $tokens_tree->[$self->{where_iter} + 2]->value();
			
			# the next symbols could be '>' , '<' , '=' , etc
			my $op = $tokens_tree->[$self->{where_iter} + 3]->value();
			
			# and after that we have an interesting situation
			# 1) we could have a basic condition: table1.column1 > "25" or table1.column1 = "winter"
			# 2) we could have a placeholder: table1.column1 > ?  or table1.column1 <= ?
			# 3) we colud have a reference to another column within the table: table1.column1 < table1.column2 (rarely seen this)
			# 4) we could have a reference to another's table value: table1.column1 > table2.column5
			# ###
			# so we should check for a type
			if ($tokens_tree->[$self->{where_iter} + 4]->type() eq 'IDENT') {
			      #print Dumper $tokens_tree; die;
			      my $refed_table = $tokens_tree->[$self->{where_iter} + 4]->value() . "." . $tokens_tree->[$self->{where_iter} + 6]->value();
			      # that's a 3d and 4th situations
			      $temp{$table_column} = {'type' => $tokens_tree->[$self->{where_iter} + 4]->type(),
						      'op' => $op,
						      'value' => $refed_table
						      };
			      $self->{where_iter} += 6;
			} else {
			      # all other situations can be summerized here
			      
                              if ($op eq 'IS' and $tokens_tree->[$self->{where_iter} + 4]->value() eq 'NOT') {
                                    $temp{$table_column} = {
                                          'type' => $tokens_tree->[$self->{where_iter} + 5]->type(),
                                          'op' => $op . " " . $tokens_tree->[$self->{where_iter} + 4]->value(),
                                          'value' => $tokens_tree->[$self->{where_iter} + 5]->value()
                                    }
                              } else {
                                    $temp{$table_column} = {
                                          'type' => $tokens_tree->[$self->{where_iter} + 4]->type(),
					  'op' => $op,
					  'value' => $tokens_tree->[$self->{where_iter} + 4]->value()
				    };      
                              }
                              #print "Haa:" , $op , "\n";
                              #print "Goo:" , $tokens_tree->[$self->{where_iter} + 4]->value() , "\n";
                              
                              
			}
			
			push @struct , \%temp;
			
		  } elsif ($tokens_tree->[$self->{where_iter}]->type() =~ /AND_SYM|OR_SYM|NOT_SYM/) {
			push @struct , $tokens_tree->[$self->{where_iter}]->value();
		  } else {
                        #print "TYPE: " , $tokens_tree->[$self->{where_iter}]->type() , "\n";
                  }
	    }
      }

      return \@struct;
}

sub filter_conditions {
      my ($self , $tree , $callback , $extract , $inner) = @_;
      
      my $skip_next = 0;
      my @res;
      for my $entry ( @$tree ) {
	    next if $skip_next-- > 0;
	    if ( ref $entry eq 'ARRAY' ) {
		  my $tmp = $self->filter_conditions( $entry , $callback , $extract , 1 );
		  if ( !$tmp || (ref $tmp eq 'ARRAY' && !@$tmp) ) {
			pop @res;
			$skip_next = 1 unless @res;
		  } else {
			push @res, $tmp;
		  }
	    } elsif ( ref $entry eq 'HASH' ) {
		  if ( $self->$callback( $entry , $extract ) ) {
			push @res, $entry;
		  } else {
			pop @res;
			$skip_next = 1 unless @res;
		  }
	    } else {
		  push @res, $entry;
	    }
      }
      
      return $res[0] if @res == 1 && ($inner || ref $res[0] eq 'ARRAY');
      return \@res;
}

sub table_comp {
      my ($self , $hashref , $extract) = @_;
      
      my @key = keys %{$hashref};
      my @table_column = split /\./ , $key[0];
      if ($table_column[0]) {
	    return 1 if ($self->cmp_names($table_column[0] , $extract));
      }
}

sub known_cond {
      my ($self , $hashref , $extract) = @_;
      
      my @key = keys %{$hashref};
      return 0 if ($hashref->{$key[0]}->{type} =~ /PARAM_MARKER|IDENT/);
      return $self->table_comp($hashref , $extract);
}

sub unknown_cond {
      my ($self , $hashref) = @_;
      
      my @key = keys %{$hashref};
      return 1 if ($hashref->{$key[0]}->{type} =~ /PARAM_MARKER|IDENT/);
}

sub unknown_conds {
      my ($self , $tree , $extract , $inner) = @_;
      
      my @res;
      for my $entry ( @$tree ) {
	    if ( ref $entry eq 'ARRAY' ) {
		  my $tmp = $self->unknown_conds( $entry , $extract , 1 );
		  push @res, @$tmp;
	    } elsif ( ref $entry eq 'HASH' ) {
		  if ( $self->unknown_cond($entry) and $self->table_comp($entry , $extract) ) {
			push @res, $self->get_content($entry);
		  } 
	    } 
      }
      
      return \@res;
}

sub get_content {
      my ($self , $hashref) = @_;
      
      my @key = keys %{$hashref};
      my @table_column = split /\./ , $key[0];
      
      my $res;
      $res .= $key[0];
      $res .= " $hashref->{$key[0]}->{op} ";
      $res .= "'" if ($hashref->{$key[0]}->{type} eq 'TEXT_STRING');
      $res .= $hashref->{$key[0]}->{value};
      $res .= "'" if ($hashref->{$key[0]}->{type} eq 'TEXT_STRING');
      
      return $res;
}

sub cond_type {
      my ($self , $hashref) = @_;
      
      return unless ref $hashref eq 'HASH';
      my @key = keys %{$hashref};
      return $hashref->{$key[0]}->{type};
}

sub col {
      my ($self , $hashref) = @_;
      
      my @key = keys %{$hashref};
      my @table_column = split /\./ , $key[0];
      
      return $table_column[1];
}

sub build_where_sql_class {
      my ($self , $tree , $inner) = @_;
      
      my $where = SQL::Builder::Where->new();
      $where->joiner("");
      $where->options(brackets => 1) if ($inner);
      
      for my $entry ( @$tree ) {
	    if ( ref $entry eq 'ARRAY' ) {
		  my $tmp = $self->build_where_sql_class( $entry , 1 );
		  $where->add($tmp);
	    } elsif ( ref $entry eq 'HASH' ) {
		  my $content = $self->get_content( $entry );
		  $where->add( $content );
		  my $type_of_condition = $self->cond_type($entry);
		  if ($type_of_condition =~ /IDENT|PARAM/) {
			$self->cond_type( $self->col($entry) , $type_of_condition );
		  }
	    } else {
		  $where->add( $entry );
	    }
      }
      
      return $where;
}

1;

=head1 NAME

MySQL::Query::Extract::WHERE

=head1 SYNOPSIS

      my $extract = MySQL::Query::Extract->new;
      $extract->init("SELECT t1.name , t2.salary FROM names AS t1 JOIN salaries AS t2 ON t1.id = t2.id WHERE t2.salary > 1000");
      
      my @tables = $extract->get_tables;
      while (my $table = $extract->next_table) {
	    my $sql_builder = $extract->extract($table);
	    print $sql_builder->sql() , "\n";
      }

=head1 DESCRIPTION

=head1 METHODS

=head2 new()

=head2 init()

=head2 extract()

=head2 known_conditions()

=head2 unknown_conditions()